package cn.iocoder.yudao.module.security.service.whitelistoperate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.system.OperatingSystemUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.security.service.dashboard.SecurityCommonService;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.AggregationBuilders;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.TermsAggregation;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.util.NamedValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.security.controller.admin.whitelistoperate.vo.*;
import cn.iocoder.yudao.module.security.dal.dataobject.whitelistoperate.WhitelistOperateDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.security.dal.mysql.whitelistoperate.WhitelistOperateMapper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.security.enums.ErrorCodeConstants.*;

/**
 * 白名单操作 Service 实现类
 *
 * @author cai
 */
@Slf4j
@Service
@Validated
public class WhitelistOperateServiceImpl implements WhitelistOperateService {

    @Resource
    private WhitelistOperateMapper whitelistOperateMapper;

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private SecurityCommonService securityCommonService;

    @Value("${elastalert.rule.file-path}")
    private String inputPath;

    @Override
    public Long createWhitelistOperate(WhitelistOperateSaveReqVO createReqVO) {
        // 插入
        WhitelistOperateDO whitelistOperate = BeanUtils.toBean(createReqVO, WhitelistOperateDO.class);
        whitelistOperate.setStartTime(LocalDateTimeUtil.now());
        whitelistOperateMapper.insert(whitelistOperate);
        // 返回
        return whitelistOperate.getId();
    }

    @Override
    public void updateWhitelistOperate(WhitelistOperateSaveReqVO updateReqVO) {
        // 校验存在
        validateWhitelistOperateExists(updateReqVO.getId());
        // 更新
        WhitelistOperateDO updateObj = BeanUtils.toBean(updateReqVO, WhitelistOperateDO.class);
        whitelistOperateMapper.updateById(updateObj);
    }

    @Override
    public void deleteWhitelistOperate(Long id) {
        // 校验存在
        validateWhitelistOperateExists(id);
        // 删除
        whitelistOperateMapper.deleteById(id);
    }

    private void validateWhitelistOperateExists(Long id) {
        if (whitelistOperateMapper.selectById(id) == null) {
            throw exception(WHITELIST_OPERATE_NOT_EXISTS);
        }
    }

    @Override
    public WhitelistOperateDO getWhitelistOperate(Long id) {
        return whitelistOperateMapper.selectById(id);
    }

    @Override
    public PageResult<WhitelistOperateDO> getWhitelistOperatePage(WhitelistOperatePageReqVO pageReqVO) {
        return whitelistOperateMapper.selectPage(pageReqVO);
    }

    @Override
    public Long startWhitelistOperate() {

        WhitelistOperateDO whitelistOperateDO = getLatestWhitelistOperate();
        if (Objects.isNull(whitelistOperateDO)) {
            throw exception(WHITELIST_OPERATE_NOT_EXISTS);
        } else {
            List<String> list = getWhitelistEventAgg(whitelistOperateDO);
            log.info("获取到白名单事件数量：{}", list.size());
            updateYaml(inputPath, list);
            String processName = "elastalert";
            if (OperatingSystemUtil.isLinux()) {
                log.info("Linux系统，执行命令：{}", processName);
                if (!isProcessRunning(processName)) {
                    log.info("{}进程没启动, 执行启动命令", processName);
                    startAlertProcess();
                } else {
                    log.info("{}进程已启动", processName);
                }
            } else {
                log.info("Windows系统，不执行命令：{}", processName);
            }
            whitelistOperateDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
            whitelistOperateMapper.updateById(whitelistOperateDO);
            return whitelistOperateDO.getId();
        }
    }

    private void startAlertProcess() {
        //ProcessBuilder builder = new ProcessBuilder(
        //        "/usr/local/bin/elastalert", "--config", "/usr/local/bin/config.yaml", "--verbose", "&"); // 获取Java进程详细信息
        //exec(builder);
        String command = "/usr/local/bin/elastalert --config /usr/local/bin/config.yaml --verbose >> /var/log/elastalert.log 2>&1 &";

        // 使用ProcessBuilder启动bash执行命令
        ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", command);

        // 可选：重定向输出流到文件（或使用DISCARD丢弃输出）
        processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);

        // 启动进程
        try {
            final Process process = processBuilder.start();

            // 必须手动读取/关闭流（否则可能挂起）
            new Thread(() -> {
                try {
                    process.getInputStream().close();
                    process.getErrorStream().close();
                    process.getOutputStream().close();
                } catch (IOException e) {
                    log.error("Failed to read process output: {}", command, e);
                }
            }).start();
        } catch (IOException e) {
            log.error("Failed to start process: {}", command, e);
        }
    }

    @Override
    public Long stopWhitelistOperate() {
        WhitelistOperateDO whitelistOperateDO = getLatestWhitelistOperate();
        if (Objects.isNull(whitelistOperateDO)) {
            throw exception(WHITELIST_OPERATE_NOT_EXISTS);
        } else {
            if (OperatingSystemUtil.isLinux()) {
                String processName = "elastalert";
                killProcess(processName);
            }
            whitelistOperateDO.setStatus(CommonStatusEnum.DISABLE.getStatus());
            whitelistOperateMapper.updateById(whitelistOperateDO);
            return whitelistOperateDO.getId();
        }
    }

    @Override
    public WhitelistOperateDO getLatestWhitelistOperate() {
        List<WhitelistOperateDO> list = whitelistOperateMapper.selectList(
                new LambdaQueryWrapperX<WhitelistOperateDO>()
                        //.eq(WhitelistOperateDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                        .orderByDesc(WhitelistOperateDO::getCreateTime)
        );
        if (CollectionUtil.isEmpty(list)) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public List<String> getWhitelistEventAgg() {
        WhitelistOperateDO latestWhitelistOperate = getLatestWhitelistOperate();
        return getWhitelistEventAgg(latestWhitelistOperate);
    }

    public List<String> getWhitelistEventAgg(WhitelistOperateDO latestWhitelistOperate) {
        if (Objects.isNull(latestWhitelistOperate)) {
            return Collections.emptyList();
        }

        String startTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getStartTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";
        String endTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getEndTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";
        log.info("聚合白名单, startTimeStr:{}, endTimeStr:{}", startTimeStr, endTimeStr);

        // 构建复合查询
        FieldValue s7COMM = FieldValue.of("S7COMM");
        FieldValue dnp3 = FieldValue.of("DNP3");
        FieldValue modbus = FieldValue.of("MODBUS");
        List<FieldValue> list = new ArrayList<>();
        list.add(s7COMM);
        list.add(dnp3);
        list.add(modbus);
        // 4. 构建聚合分析
        TermsAggregation termsAggregation = AggregationBuilders.terms()
                .field("network.main_info")
                .size(90000)
                .order(new NamedValue<SortOrder>("_count", SortOrder.Desc))
                .build();
        SearchRequest request = SearchRequest.of(s -> s
                .index(".ds-logs-pfelk-snort-*")
                //.index("elastalert_log")
                .query(q -> q
                        .bool(b -> b
                                        .filter(f -> f.terms(t -> t.field("network.app")
                                                .terms(v -> v.value(list))))
                                        .filter(f -> f.range(r -> r.date(a -> a.field("@timestamp")
                                                .gte(startTimeStr)
                                                .lte(endTimeStr))))
                                //.filter(f -> f.term(t -> t.field("elastalert.alert").value(0)))
                        )
                )
                .aggregations("main_info_analysis", termsAggregation._toAggregation())
                .size(0)
        );
        List<String> list2 = new ArrayList<>();
        // 执行并处理结果
        try {
            SearchResponse<Object> response = elasticsearchClient.search(request, Object.class);

            // 7. 解析聚合结果
            List<StringTermsBucket> buckets = response.aggregations()
                    .get("main_info_analysis")
                    .sterms()
                    .buckets()
                    .array();
            for (StringTermsBucket bucket : buckets) {
                String key = bucket.key().stringValue();
                list2.add(key);
            }
        } catch (IOException e) {
            log.error("elasticsearch request err,request: {}", request, e);
        }

        return list2;
    }

    public void updateYaml(String inputPath, List<String> whitelist) {
        // 配置YAML格式选项
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // 块状格式
        options.setIndent(2);                                       // 缩进2空格
        options.setPrettyFlow(true);                                // 美化输出

        Yaml yaml = new Yaml(options);
        Map<String, Object> yamlMap = null;
        try (InputStream in = new FileInputStream(inputPath)) {
            // 加载原始配置（保持LinkedHashMap顺序）
            yamlMap = yaml.loadAs(in, LinkedHashMap.class);
        } catch (IOException e) {
            log.error("Failed to load YAML file: {}", inputPath, e);
        }

        try (Writer writer = new FileWriter(inputPath)) {
            yamlMap.put("whitelist", whitelist);
            // 写入更新后的配置
            yaml.dump(yamlMap, writer);

        } catch (IOException e) {
            log.error("Failed to write YAML file: {}", inputPath, e);
        }
    }

    public boolean isProcessRunning(String processName) {
        try {
            ProcessBuilder builder = createProcessBuilder(processName);
            Process process = builder.start();

            // 读取命令输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (isValidProcessLine(line, processName)) {
                        return true;
                    }
                }
            }

            // 等待命令执行完成
            int exitCode = process.waitFor();
            return exitCode == 0;
        } catch (IOException | InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    // 构建跨平台命令
    private ProcessBuilder createProcessBuilder(String processName) {
        List<String> command;
        // 排除grep自身进程
        command = Arrays.asList("sh", "-c",
                "ps -ef | grep -v grep | grep -i " + processName);
        return new ProcessBuilder(command);
    }

    // 验证进程信息行有效性
    private boolean isValidProcessLine(String line, String processName) {
        return line.contains(processName);
    }

    public void exec(ProcessBuilder builder) {
        try {
            List<String> command = builder.command();
            log.info("Executing command: {}", StringUtils.join(command, " "));
            Process process = builder.start();
        } catch (IOException e) {
            log.error("Failed to start process: {}", builder, e);
        }
    }

    public void killProcess(String processName) {
        ProcessBuilder builder = new ProcessBuilder("pkill", "-f", processName);
        exec(builder);
    }

}